package com.gsi.webstore.platform.storefront.internal.order;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.Set;

import com.gsi.webstore.platform.adapter.taxes_duties_fees.capi.calculation.ComputedTaxInfoItem;
import com.gsi.webstore.platform.storefront.capi.order.GSIExtraChargeInformationBO;
import com.gsi.webstore.platform.storefront.capi.order.GSIGiftMessageLineItemBO;
import com.gsi.webstore.platform.storefront.capi.order.GSIGiftWrapLineItemBO;
import com.gsi.webstore.platform.storefront.capi.order.GSILineItemCtnrBO;
import com.gsi.webstore.platform.storefront.capi.order.GSILineItemCtnrCalculationResultView;
import com.gsi.webstore.platform.storefront.capi.order.GSIProductLineItemBO;
import com.gsi.webstore.platform.storefront.capi.order.GSITaxInformationBO;
import com.gsi.webstore.platform.storefront.capi.order.GSIWarrantyLineItemBO;
import com.intershop.beehive.bts.capi.orderprocess.calculation.ComputedProductLineItem;
import com.intershop.beehive.core.capi.domain.AttributeValue;
import com.intershop.beehive.foundation.quantity.Money;
import com.intershop.component.catalog.capi.CatalogCategoryBO;
import com.intershop.component.foundation.capi.spreadsheet.ComputedItem;
import com.intershop.component.foundation.capi.spreadsheet.ComputedMoneyItem;
import com.intershop.component.marketing.capi.promotion.calculation.rule.ComputedDiscountItem;
import com.intershop.component.warranty.capi.WarrantyBO;
import com.intershop.sellside.appbase.b2c.capi.calculation.LineItemCtnrCalculationResultView;
import com.intershop.sellside.appbase.b2c.capi.order.Discount;
import com.intershop.sellside.appbase.b2c.capi.order.GiftMessageLineItemBO;
import com.intershop.sellside.appbase.b2c.capi.order.GiftWrapLineItemBO;
import com.intershop.sellside.appbase.b2c.capi.order.ProductLineItemBO;
import com.intershop.sellside.appbase.b2c.capi.order.ProductLineItemBOProxy;

public class GSIProductLineItemBOImpl extends ProductLineItemBOProxy implements GSIProductLineItemBO
{
    public static String SHIPPINGMETHOD_HIDE_EDD_ATTRIBUTE = "META_Hide_EDD";
    public static String PRODUCT_HIDE_EDD_ATTRIBUTE = "META_Hide_EDD";
    public static String PRODUCTTYPE_HIDE_EDD_ATTRIBUTE = "META_Hide_EDD";
    public static String EDD_FROMDATE_ATTRIBUTE = "EDD_DeliveryFromDate";
    public static String EDD_TODATE_ATTRIBUTE = "EDD_DeliveryToDate";

    private final ProductLineItemBO _delegate;
    private final GSILineItemCtnrBO _order;
    private final LineItemCtnrCalculationResultView _calculationResultView;
    private String _currency;
    private Money _basePrice;
    private Money _taxAmount;
    private Money _dutyAmount;
    private Collection<Discount> _itemDiscounts;
    private Collection<Discount> _shippingDiscounts;
    private Collection<GSITaxInformationBO> _itemPriceBasedTaxes;
    private Collection<GSITaxInformationBO> _shippingCostsBasedTaxes;
    private Collection<GSITaxInformationBO> _duties;
    private Collection<GSIExtraChargeInformationBO> _fees;
    private Collection<GSIExtraChargeInformationBO> _geographicalFees;
    private Collection<GSIExtraChargeInformationBO> _importFees;

    public GSIProductLineItemBOImpl(final ProductLineItemBO delegate, final GSILineItemCtnrBO order, final LineItemCtnrCalculationResultView resultView)
    {
        super(delegate);
        _delegate = delegate;
        _order = order;
        _calculationResultView=resultView;
    }

    @Override
    public Date getDeliveryToDate()
    {
        return getCustomAttributes().getDate(EDD_TODATE_ATTRIBUTE);
    }

    @Override
    public Date getDeliveryFromDate()
    {
        return getCustomAttributes().getDate(EDD_FROMDATE_ATTRIBUTE);
    }

    @Override
    public boolean isHiddenEDD()
    {
        if(_order.isHiddenEDD())
        {
            return true;
        }
        Integer hideEDD=getShippingMethod().getInteger(SHIPPINGMETHOD_HIDE_EDD_ATTRIBUTE);
        if(hideEDD != null && hideEDD.intValue()==1)
        {
            return true;
        }
        final AttributeValue productHideEDD= getProduct().getAttributeValue(PRODUCT_HIDE_EDD_ATTRIBUTE);
        if(productHideEDD != null && productHideEDD.getIntValue()==1)
        {
            return true;
        }
        final Iterator<? extends CatalogCategoryBO> categories=getProduct().getCatalogCategories().iterator();
        while(categories.hasNext())
        {
            hideEDD=categories.next().getInteger(PRODUCTTYPE_HIDE_EDD_ATTRIBUTE);
            if(hideEDD != null && hideEDD.intValue()==1)
            {
                return true;
            }
        }
        return false;
    }

    /*** Calculation dependent methods ***/
    @Override
    public Collection<Discount> getAppliedItemDiscounts()
    {
        if (isFreeGift() || isHiddenGift())
        {
            return Collections.emptyList();
        }

        if (_itemDiscounts != null)
        {
            return _itemDiscounts;
        }
        final GSILineItemCtnrCalculationResultView resultView = (GSILineItemCtnrCalculationResultView)_calculationResultView;
        return _itemDiscounts = resultView.getAppliedDiscountsOnItem(_delegate.getComputedItemID());
    }

    @Override
    public Collection<Discount> getAppliedShippingDiscounts()
    {
        if (isFreeGift() || isHiddenGift())
        {
            return Collections.emptyList();
        }

        if (_shippingDiscounts != null)
        {
            return _shippingDiscounts;
        }
        final GSILineItemCtnrCalculationResultView resultView = (GSILineItemCtnrCalculationResultView)_calculationResultView;
        return _shippingDiscounts = resultView.getAppliedDiscountsOnShipping(_delegate.getComputedItemID());
    }

    @Override
    public Collection<GSITaxInformationBO> getAppliedItemDiscountTaxes(final Discount discount)
    {
        if (discount instanceof GSIDiscount)
        {
            final GSILineItemCtnrCalculationResultView resultView = (GSILineItemCtnrCalculationResultView)_calculationResultView;
            final Collection<ComputedTaxInfoItem> taxItems = resultView.getPLITaxDiscountInfoItems();

            return getDiscountTaxes(discount, taxItems);
        }
        return Collections.emptyList();
    }

    @Override
    public Money getNetDiscountAmount(final Discount discount)
    {
        if (discount instanceof GSIDiscount)
        {
            final GSILineItemCtnrCalculationResultView resultView = (GSILineItemCtnrCalculationResultView)_calculationResultView;
            final GSIDiscount gsiDiscount = (GSIDiscount)discount;
            final ComputedDiscountItem cdi = gsiDiscount.getComputedDiscountItem();

            final ComputedProductLineItem cpli = getComputedPLI();

            final Collection<ComputedMoneyItem> taxDiscountAmounts = resultView.getNetDiscountAmounts();
            for(final ComputedMoneyItem tax : taxDiscountAmounts)
            {
                if (tax.getDependsOn().contains(cpli) && tax.getDependsOn().contains(cdi))
                {
                    return tax.getMoney();
                }
            }
        }
        return Money.NOT_AVAILABLE;
    }


    @Override
    public Collection<GSITaxInformationBO> getAppliedShippingDiscountTaxes(final Discount discount)
    {
        if (discount instanceof GSIDiscount)
        {
            final GSILineItemCtnrCalculationResultView resultView = (GSILineItemCtnrCalculationResultView)_calculationResultView;
            final Collection<ComputedTaxInfoItem> taxItems = resultView.getShippingTaxDiscountInfoItems();

            return getDiscountTaxes(discount, taxItems);
        }
        return Collections.emptyList();
    }

    private Collection<GSITaxInformationBO> getDiscountTaxes(final Discount discount,
                    final Collection<ComputedTaxInfoItem> taxItems)
    {
        final GSIDiscount gsiDiscount = (GSIDiscount)discount;
        final ComputedDiscountItem cdi = gsiDiscount.getComputedDiscountItem();

        final Collection<GSITaxInformationBO> result = new ArrayList<GSITaxInformationBO>();
        for(final ComputedTaxInfoItem item : taxItems)
        {
            final Set<ComputedItem> dependsOn = item.getDependsOn();
            if (dependsOn.contains(cdi) && dependsOn.contains(getComputedPLI()))
            {
                result.add(new GSITaxInformationBOImpl(item));
            }
        }
        return result;
    }

    public Money getTaxAmount()
    {
        if (_taxAmount != null)
        {
            return _taxAmount;
        }
        return ((GSILineItemCtnrCalculationResultView)_calculationResultView).getTaxAmount(_delegate.getComputedItemID());
    }

    @Override
    public Collection<GSITaxInformationBO> getItemPriceBasedTaxes()
    {
        if (isFreeGift() || isHiddenGift())
        {
            return Collections.emptyList();
        }

        if (_itemPriceBasedTaxes != null)
        {
            return _itemPriceBasedTaxes;
        }
        return _itemPriceBasedTaxes = ((GSILineItemCtnrCalculationResultView)_calculationResultView).getItemPriceBasedTaxes(_delegate.getComputedItemID());
    }

    @Override
    public Collection<GSITaxInformationBO> getShippingCostBasedTaxes()
    {
        if (isFreeGift() || isHiddenGift())
        {
            return Collections.emptyList();
        }

        if (_shippingCostsBasedTaxes != null)
        {
            return _shippingCostsBasedTaxes;
        }
        return _shippingCostsBasedTaxes = ((GSILineItemCtnrCalculationResultView)_calculationResultView).getShippingCostBasedTaxes(_delegate.getComputedItemID());
    }

    @Override
    public Money getDutyAmount()
    {
        if (_dutyAmount != null)
        {
            return _dutyAmount;
        }
        return ((GSILineItemCtnrCalculationResultView)_calculationResultView).getDutyAmount(_delegate.getComputedItemID());
    }

    @Override
    public Collection<GSITaxInformationBO> getDuties()
    {
        if (isFreeGift() || isHiddenGift())
        {
            return Collections.emptyList();
        }

        if (_duties != null)
        {
            return _duties;
        }
        return _duties = ((GSILineItemCtnrCalculationResultView)_calculationResultView).getDuties(_delegate.getComputedItemID());
    }

    @Override
    public Collection<GSIExtraChargeInformationBO> getFees()
    {
        if (isFreeGift() || isHiddenGift())
        {
            return Collections.emptyList();
        }
        if (_fees != null)
        {
            return _fees;
        }
        _fees = new ArrayList<GSIExtraChargeInformationBO>();
        _fees.addAll(getGeographicalFees());
        _fees.addAll(getImportFees());
        return _fees;
    }

    @Override
    public Collection<GSIExtraChargeInformationBO> getGeographicalFees()
    {
        if (_geographicalFees != null)
        {
            return _geographicalFees;
        }
        return _geographicalFees = ((GSILineItemCtnrCalculationResultView)_calculationResultView).getGeographicalFees(_delegate.getComputedItemID());
    }

    @Override
    public Collection<GSIExtraChargeInformationBO> getImportFees()
    {
        if (_importFees != null)
        {
            return _importFees;
        }
        return _importFees = ((GSILineItemCtnrCalculationResultView)_calculationResultView).getImportFees(_delegate.getComputedItemID());
    }

    @Override
    public GSIWarrantyLineItemBO getWarranty()
    {
        if (hasWarranty())
        {
            return new GSIWarrantyLineItemBOImpl(_delegate.getWarranty(), _calculationResultView);
        }
        else
        {
            return null;
        }
    }

    @Override
    public GSIWarrantyLineItemBO addWarranty(final WarrantyBO aWarranty)
    {
        return new GSIWarrantyLineItemBOImpl(_delegate.addWarranty(aWarranty), _calculationResultView);
    }

    @Override
    public GSIGiftWrapLineItemBO getGiftWrap()
    {
        final GiftWrapLineItemBO giftWrapBO = _delegate.getGiftWrap();
        return new GSIGiftWrapLineItemBOImpl(giftWrapBO, _order, _calculationResultView);
    }

    @Override
    public GSIGiftMessageLineItemBO getGiftMessage()
    {
        final GiftMessageLineItemBO giftMessageBO = _delegate.getGiftMessage();
        return new GSIGiftMessageLineItemBOImpl(giftMessageBO, _order, _calculationResultView);
    }

    @Override
    public Money getNetBasePrice()
    {
        return ((GSILineItemCtnrCalculationResultView)_calculationResultView).getPLINetBasePrice(_delegate.getComputedItemID());
    }

    @Override
    public Money getNetShippingCosts()
    {
        return ((GSILineItemCtnrCalculationResultView)_calculationResultView).getShippingPLINetCosts(_delegate.getComputedItemID());
    }
}